package com.bckj.fastboot.iot.biz.service.impl;

import com.bckj.fastboot.core.extra.cache.EntityCacheOps;
import com.bckj.fastboot.core.jackson.JsonUtils;
import com.bckj.fastboot.iot.biz.repository.IotDeviceRepository;
import com.bckj.fastboot.iot.biz.service.IotDeviceLifecycleService;
import com.bckj.fastboot.iot.common.constant.IotAlarmConstants;
import com.bckj.fastboot.iot.common.constant.IotDeviceConstants;
import com.bckj.fastboot.iot.common.dto.ctx.IotDeviceContext;
import com.bckj.fastboot.iot.common.dto.msg.IotProtocolDataMessage;
import com.bckj.fastboot.iot.common.dto.request.IotDeviceStatusRecordRequest;
import com.bckj.fastboot.iot.common.entity.IotDevice;
import com.bckj.fastboot.iot.common.enums.IotDeviceEvent;
import com.bckj.fastboot.iot.common.enums.IotDeviceState;
import com.bckj.fastboot.iot.common.mq.producer.IotProtocolDataMessageProducer;
import com.bckj.fastboot.iot.common.repository.IotDeviceStatusRecordRepository;
import com.bckj.fastboot.iot.biz.service.IotDeviceFireEventService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Map;

/**
 * IotDeviceStatusServiceImpl
 *
 * @author wfc
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class IotDeviceLifecycleServiceImpl implements IotDeviceLifecycleService {

    private final IotDeviceRepository iotDeviceRepository;
    private final EntityCacheOps entityCacheOps;
    private final IotDeviceFireEventService iotDeviceFireEventService;
    private final IotDeviceStatusRecordRepository iotDeviceStatusRecordRepository;
    private final IotProtocolDataMessageProducer iotProtocolDataMessageProducer;

    @Override
    public void onDeviceHeartbeat(IotDeviceContext ctx) {
        Object heartbeatTime = ctx.getHeartbeatTime();
        if (heartbeatTime == null) {
            heartbeatTime = LocalDateTime.now();
        }
        boolean update = iotDeviceRepository.lambdaUpdate()
                .set(IotDevice::getLastLiveTime, heartbeatTime)
                .set(IotDevice::getUpdateTime, LocalDateTime.now())
                .eq(IotDevice::getId, ctx.getId())
                .update();
        if (update) {
            // 判断当前设备状态，如果是离线状态，则触发上线
            var deviceWrapper = entityCacheOps.wrap(IotDevice.class, ctx.getId()).orThrow();
            Integer status = deviceWrapper.getInteger(IotDevice::getStatus);
            if (status == IotDeviceConstants.STATUS_OFFLINE) {
                iotDeviceFireEventService.fire(IotDeviceState.OFFLINE, IotDeviceEvent.CONNECT, ctx);
            }
        }
    }

    @Override
    public void onDeviceOnline(IotDeviceContext ctx) {
        boolean update = updateDeviceStatus(ctx);
        if (update) {
            // 触发掉线恢复
            handleIotAlarm(ctx, false);
        }
    }

    @Override
    public void onDeviceOffline(IotDeviceContext ctx) {
        boolean update = updateDeviceStatus(ctx);
        if (update) {
            // 触发掉线报警
           handleIotAlarm(ctx, true);
        }
    }

    private void handleIotAlarm(IotDeviceContext ctx, boolean isOffline) {
        var message = IotProtocolDataMessage
                .ofJson(null,
                        ctx.getDevCode(),
                        JsonUtils.toJsonStr(Map.of(IotAlarmConstants.ALARM_TYPE_OFFLINE, isOffline)));
        iotProtocolDataMessageProducer.send(message);
    }

    @Override
    public void onDeviceFault(IotDeviceContext ctx) {
        boolean update = updateDeviceStatus(ctx);
    }

    @Override
    public void onDeviceRepair(IotDeviceContext ctx) {
        boolean update = updateDeviceStatus(ctx);
    }

    @Override
    public void onDeviceDisable(IotDeviceContext ctx) {
        boolean update = updateDeviceStatus(ctx);
    }

    @Override
    public void onDeviceEnable(IotDeviceContext ctx) {
        boolean update = updateDeviceStatus(ctx);
    }

    private boolean updateDeviceStatus(IotDeviceContext ctx) {
        boolean update = iotDeviceRepository.lambdaUpdate()
                .set(IotDevice::getStatus, ctx.getTo().getStatus())
                .set(IotDevice::getUpdateTime, LocalDateTime.now())
                .eq(IotDevice::getId, ctx.getId())
                .update();
        if (update) {
            // 保存设备状态记录
            saveDeviceStatusRecord(ctx);
        }
        return update;
    }

    private void saveDeviceStatusRecord(IotDeviceContext ctx) {
        iotDeviceStatusRecordRepository.save(
                IotDeviceStatusRecordRequest.create()
                        .setDevId(ctx.getId())
                        .setDevCode(ctx.getDevCode())
                        .setFromStatus(ctx.getFrom().getStatus())
                        .setToStatus(ctx.getTo().getStatus())
        );
    }
}
